#include <GameEngine/Graphics/Meshes/trgl_mesh_subgroup.h>

#include <GameEngine/Graphics/Meshes/trgl_mesh.h>
#include <GameEngine/Geometry/Surfaces/trgl3d.h>
#include <GameEngine/Graphics/openGL.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================

//! Default constructor
TrglMeshSubGroup::TrglMeshSubGroup() : use_transparency_ (false) {}

//! Copy constructor
/*!
 @param rhs The triangle subgroup to copy
 */
TrglMeshSubGroup::TrglMeshSubGroup(const TrglMeshSubGroup& rhs) : trgls_(rhs.trgls_), material_(rhs.material_), use_transparency_(rhs.use_transparency_) {}

//! Destructor
TrglMeshSubGroup::~TrglMeshSubGroup() {}

//! Assignment operator
/*!
 @param rhs The triangle subgroup to copy
 */
TrglMeshSubGroup& TrglMeshSubGroup::operator=(const TrglMeshSubGroup& rhs) {
	if ( &rhs != this ) {
		trgls_ = rhs.trgls_;
		material_ = rhs.material_;
		use_transparency_ = rhs.use_transparency_;
	}
	return *this;
}

//! Accessor to the material of the triangle subgroup (read only)
/*!
 @return A constant reference to the material of the triangle subgroup
 */
const Material& TrglMeshSubGroup::material() const {
	return material_;
}

//! Accessor to the material of the triangle subgroup (read-write)
/*!
 @return A reference to the material of the triangle subgroup
 */
Material& TrglMeshSubGroup::material() {
	return material_;
}

//! Accessor to the triangles of the triangle subgroup (read only)
/*!
 @return A constant reference to the triangles of the triangle subgroup
 */
const std::vector<IndexedTrgl>& TrglMeshSubGroup::triangles() const {
	return trgls_;
}

//! Accessor to the triangles of the triangle subgroup (read-write)
/*!
 @return A reference to the triangles of the triangle subgroup
 */
std::vector<IndexedTrgl>& TrglMeshSubGroup::triangles() {
	return trgls_;
}

//! Accessor to the use transparency flag of the triangle subgroup (read only)
/*!
 @return The use transparency flag of the triangle subgroup
 */
bool TrglMeshSubGroup::use_transparency() const {
	return use_transparency_;
}

//! Accessor to the material of the triangle subgroup (read-write)
/*!
 @return A reference to the use transparency flag of the triangle subgroup
 */
bool& TrglMeshSubGroup::use_transparency() {
	// Texture is transparent
	return use_transparency_;
}

//! Test if the subgroup uses transparency, either because of its material or because of its use transparency flag
/*!
 @return True if the subgroup uses transparency, false otherwise
 */
bool TrglMeshSubGroup::has_transparency() const {
	// All vertices are transparent
	if ( material_.active_parameter(Material::AlphaMaterial) == true ) {
		if ( material_.alpha() < 1 ) return true;
	}

	// Return the use transparency flag
	return use_transparency();
}

//! Accessor to the number of faces in the triangle subgroup
/*!
 @return The number of faces in the triangle subgroup
 */
SurfaceMesh::FaceCount TrglMeshSubGroup::face_count() const {
	return (SurfaceMesh::FaceCount)trgls_.size();
}

//! Clear the triangle subgroup
/*!
 *  This function removes all the triangles from the triangle subgroup, resets its
 *  use transparency flag to false and resets its material.
 */
void TrglMeshSubGroup::clear() {
	trgls_.clear();
	material_ = Material();
	use_transparency_ = false;
}

//! Render the subgroup
/*!
 @param ray The picking ray under the mouse
 @param mesh The mesh to which the subgroup belongs
 @return Always return true
 */
bool TrglMeshSubGroup::render_subgroup(const Line3d<float>& ray, const TrglMesh& mesh) {
	// Useful variables
	int indexes[3];
	int nindexes[3];
	int cindexes[3];
	
	// Get the size of the vertices, normals and texture arrays
	int sz = (int)mesh.vertices_.size();
	int szn = (int)mesh.normals_.size();
	int szc = (int)mesh.coords_.size();

	// Setup OpenGL
	glColor3f(1.0f,1.0f,1.0f);
	glEnable(GL_TEXTURE_2D);
	material_.apply_material();

	// Iterate over the triangles
	for ( std::vector<IndexedTrgl>::const_iterator it = trgls_.begin(); it != trgls_.end(); ++it) {
		// Start drawing the triangle
		glBegin(GL_TRIANGLE_FAN);

		// Build indexes
		indexes[0] = it->index1();
		indexes[1] = it->index2();
		indexes[2] = it->index3();
		if ( it->has_normals() ) {
			nindexes[0] = it->nindex1();
			nindexes[1] = it->nindex2();
			nindexes[2] = it->nindex3();
		}
		else {
			nindexes[0] = szn;
			nindexes[1] = szn;
			nindexes[2] = szn;

			// If no normals are specified, do polygon-based shading
			Trgl3d<float> trgl(mesh.vertices_[indexes[0]], mesh.vertices_[indexes[1]], mesh.vertices_[indexes[2]]);
			Vector3d<float> n = trgl.normal();
			glNormal3f(n.x(), n.y(), n.z());
		}
		if ( it->has_texture_coordinates() ) {
			cindexes[0] = it->cindex1();
			cindexes[1] = it->cindex2();
			cindexes[2] = it->cindex3();
		}
		else {
			cindexes[0] = szc;
			cindexes[1] = szc;
			cindexes[2] = szc;
		}

		// Draw vertices
		for ( int k = 0; k < 3; ++k ) {
			// Check if current vertex is valid
			if ( indexes[k] >= sz ) break;

			// Check if vertex has an associated normal
			if ( nindexes[k] < szn ) {
				const Vector3d<float>& n = mesh.normals_[nindexes[k]];
				glNormal3f(n.x(), n.y(), n.z());
			}

			// Check if vertex has an associated texture coordinate
			if ( cindexes[k] < szc ) {
				const TextureCoordinate2d& tc = mesh.coords_[cindexes[k]];
				tc.apply_coordinates();
			}

			// Draw vertex
			const Point3d<float>& pt = mesh.vertices_[indexes[k]];
			glVertex3f(pt.x(), pt.y(), pt.z());
		}

		// Finish drawing the triangle
		glEnd();
	}

	// Remove material
	material_.remove_material();

	return true;
}

//=================================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
